/*
 * Copyright (C) 2017 Intel Corporation.  All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */

// Copyright (c) WanSheng Intelligent Corp. All rights reserved.


#ifndef COAP_EXTENSION_COAP_EXT_H_
#define COAP_EXTENSION_COAP_EXT_H_

#include "er-coap.h"
#include "coap_request.h"
#include "logs.h"

#ifdef __cplusplus
extern "C" {
#endif

#define COAP_FLAG_DEBUG         0x00000001

#define COAP_FLAG_REQUEST       0x00000200
#define COAP_FLAG_TRANSACTION   0x00000400
#define COAP_FLAG_ENGINE        0x00000800
#define COAP_FLAG_BLOCKWISE     0x00001000

#define COAP_FLAG_PACK          0x00010000
#define COAP_FLAG_ENTRY         0x10000000



#define COAP_INFO(flag,...) if((log_tag_mask[LOG_COAP_MASK_ID] & flag) && (LOG_INFO >= log_level)) log_print( __FUNCTION__, __VA_ARGS__)
#define COAP_VERBOS(flag,...) if((log_tag_mask[LOG_COAP_MASK_ID] & flag) && (LOG_VERBOSE >= log_level)) log_print(__FUNCTION__, __VA_ARGS__)
#define COAP_DEBUG(...) if((log_tag_mask[LOG_COAP_MASK_ID] & COAP_FLAG_DEBUG) &&  LOG_DEBUG >= log_level) log_print(__FUNCTION__, __VA_ARGS__)




#define COAP_LOG_VERBOSE 4
#define COAP_LOG_INFO 3
#define COAP_LOG_WARNING 2
#define COAP_LOG_ERROR 1


#ifndef COAP_DEBUG_FLAG
#define COAP_DEBUG_FLAG 1
#endif

#define COAP_EXPIRY_UNDEF ((uint32_t) -1)


extern uint32_t coap_debug_flags;
extern FILE * g_log_stream;

#if (COAP_DEBUG_FLAG==1)
#include <stdio.h>

#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif


#define MAX_COAP_ACTIONS 5
extern const char* SZ_COAP_ACTION[MAX_COAP_ACTIONS];

#define COAP_TYPE_MAX 4
extern const char * SZ_COAP_TYPE[COAP_TYPE_MAX];

const char * coap_get_request_name(u_int16_t code);


#define  NO_SOCKET -1
/*
    @param socket:     the socket used for sending packet
                        NO_SOCKET(-1) - means a random socket will be used.
*/
void wa_coap_send_packet_sockaddr(int socket, coap_packet_t *request, struct sockaddr_in * dest_addr);

void wa_coap_send_packet_addr(int socket, coap_packet_t *request, net_addr_t * dest_addr);


void wa_coap_send_packet_IP(coap_packet_t * request, const char * IP, int port);


//
// blocking_coap_request.c
//

void set_coap_debug_stream(void *);
void * get_coap_debug_stream();
void set_coap_debug_flags(uint32_t flags);
void init_coap_ext();
void coap_auto_token(coap_packet_t * message);

int handle_response_blockwise(coap_request_user_data_t * data, coap_packet_t *response) ;
void free_coap_request_user_data(coap_request_user_data_t * data);
coap_request_user_data_t * make_blocking_request(
        coap_context_t * coap_ctx,
        coap_packet_t *request, uip_ipaddr_t * server_addr);

/*
    @brief      use a internal opened socket for sending the request and recieving the response
*/
coap_request_user_data_t * make_blocking_request_alone(
        coap_context_t *coap_ctx_incoming, coap_packet_t *request, 
        uip_ipaddr_t * server_addr);

int make_blocking_request_to_file(
        coap_context_t * coap_ctx,
        coap_packet_t *request,
        char * filename,
        uip_ipaddr_t * server_addr);

int coap_blocking_request(
                        coap_context_t *coap_ctx,
                        uip_ipaddr_t *dst_addr,
                        coap_packet_t *request,
                        restful_response_handle_f request_callback,
                        void * user_data);

/*
    @brief  use internally open socket to finish the job  
*/
int coap_blocking_request_alone(
        coap_context_t *coap_ctx_incoming,
        uip_ipaddr_t *dst_addr,
        coap_packet_t *request,
        restful_response_handle_f request_callback,
        void * user_data);    

void coap_nonblocking_request(
				coap_context_t *coap_ctx,
				uip_ipaddr_t *dst_addr,
				coap_packet_t *request,
				restful_response_handle_f request_callback,
				void * user_data);

coap_status_t coap_parse_message_tcp(void *packet, uint8_t *data, uint16_t data_len);
int coap_blocking_request_tcp(coap_context_t *coap_ctx,coap_packet_t *request,
		restful_response_handle_f request_callback,void * user_data);
size_t coap_serialize_message_tcp(void *packet, uint8_t ** buffer_out);
int coap_set_payload_tcp(void *packet, const void *payload, size_t length);
uint8_t coap_is_request(coap_packet_t * coap_message);



typedef struct
{
    int total_size;
    int downloaded;
    time_t start_time;
    time_t end_time;
    char  error_text[128];
} download_status_t;
// return code:
#define BLOCK2_NOT_SUPPORTED (-100)
int coap_tcp_get_into_file(
        char * server_ip,
        int port,
        char * url,
        char * query,
        coap_packet_t *coap_pkt,   //response
        char * store_path,
        download_status_t * download_status);  // can be NULL

uint16_t coap_find_mid(uint8_t *buffer);
uint8_t coap_find_code(uint8_t *buffer);
void coap_change_mid(uint8_t *buffer, uint16_t id);
coap_resource_handler_t * coap_find_resource(coap_context_t *coap_ctx, const char *url_str);
int add_resource_handler(coap_context_t * coap_ctx, coap_resource_handler_t * handler);
void link_resource_handler(coap_resource_handler_t ** head, coap_resource_handler_t * handler);
uint32_t check_blockwise_timeout_ms(coap_context_t * coap_ctx,  int timeout_sec);
int set_res_blockwise(coap_context_t * coap_ctx,  char * url_allocated, void * buffer,
        int buffer_size, uint32_t block_num, uint16_t content_fmt,
        struct  net_addr *peer_addr, uint8_t token_len, uint8_t * toke);
int send_coap_msg(coap_context_t * coap_ctx, coap_packet_t * message);
int send_coap_msg2(coap_context_t *coap_ctx, coap_packet_t *message, struct net_addr * dest_addr );

uint8_t coap_handle_request_internal(coap_context_t * coap_ctx, coap_packet_t * request,
		coap_packet_t * response, char ** out_payload, int * payload_len);
char * coap_get_full_url_alloc(coap_packet_t * request);

int coap_handle_packet(coap_context_t *coap_ctx);

coap_packet_t * coap_clone_message(coap_packet_t * message);

void coap_free_cloned_message(coap_packet_t * message);


struct _coap_request_env;
typedef struct _coap_request_env * coap_request_env_t;
coap_request_env_t coap_create_env(coap_packet_t * request, coap_context_t * context);
coap_request_env_t coap_clone_env(coap_request_env_t exist_env);
coap_request_env_t coap_env_increase_ref(coap_request_env_t env);

void coap_free_env(coap_request_env_t env);
void coap_env_feed_response(coap_request_env_t env, char * payload, int payload_len, int code, int fmt);
const char * coap_env_get_url(coap_request_env_t env);
void coap_env_set_addr(coap_request_env_t env, struct net_addr * addr);
char * coap_env_description(coap_request_env_t env, char * buffer, int len);
void coap_env_set_acked(coap_request_env_t env, bool acked);

#ifdef __cplusplus
}
#endif
#endif /* COAP_EXTENSION_COAP_EXT_H_ */
